/*
 *  materiau.cpp
 *  Ray Tracing
 *
 *  Definition des materiaux et de leur propriete optique
 *
 *  Created by Jean-Marc Comby + Romain Dequesne on Thu Mar 25 2004.
 *  Copyright (c) 2004. All rights reserved.
 *
 */
#include "Materiau.hpp"

#include "Utils/borne.hpp"

#include <iostream>
#include <cmath>

using namespace std;

/**
 * Constructeur usuel, qui initialise tous les champs du Materiau. Ici
 * on ajoute un bruit sur la normale.
 *
 * @param refl coefficient de réflexion, automatiquement borné.
 * @param trans coefficient de transparence, automatiquement borné.
 * @param indice indice de réfraction, automatiquement borné.
 * @param reg coefficient de diffusion, automatiquement borné.
 * @param bruit amplitude de la perturbation sur la normale.
 * @param c Couleur du Materiau.
 */
Materiau::Materiau(const double& refl, const double& trans,
		   const double& indice, const double& reg,
		   const double& bruit, const Couleur& c)
  : Reflexivite( borne( refl, 0.0, 1.0 ) ),
    Transparence( borne( trans, 0.0, 1.0 ) ),
    Indice( borne( indice, 0.0, 100.0 ) ),
    Regularite( borne( reg, 0.0, 1.0 ) ),
    Bruit( borne( bruit, 0.0, 1.0 ) )
{}

/**
 * Constructeur usuel, qui initialise tous les champs du Materiau.
 *
 * La version par défaut crée un Materiau de couleur noire, avec tous
 * ses coefficients à 0 et un indice de réfraction à 1.
 *
 * @param refl coefficient de réflexion, automatiquement borné.
 * @param trans coefficient de transparence, automatiquement borné.
 * @param indice indice de réfraction, automatiquement borné.
 * @param reg coefficient de diffusion, automatiquement borné.
 * @param c Couleur du Materiau.
 */
Materiau::Materiau(const double& refl, const double& trans,
		   const double& indice, const double& reg,
		   const Couleur& c)
  : Reflexivite( borne( refl, 0.0, 1.0 ) ),
    Transparence( borne( trans, 0.0, 1.0 ) ),
    Indice( borne( indice, 0.0, 100.0 ) ),
    Regularite( borne( reg, 0.0, 1.0 ) ),
    Bruit( 0. )
    //le tout est borne pour ne pas prendre de rique
{}


Materiau::Materiau( const double& refl, const double& trans,
		    const double& indice, const double& reg,
		    const double& bruit, const TypeMateriau& type )
  : Reflexivite( borne( refl, 0.0, 1.0 ) ),
    Transparence( borne( trans, 0.0, 1.0 ) ),
    Indice( borne( indice, 0.0, 100.0 ) ),
    Regularite( borne( reg, 0.0, 1.0 ) ),
    Bruit( 0. ), Type( type )
{}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
Materiau::~Materiau()
{}

/**
 * Permet de récupérer la valeur du coefficient de réflexion.
 *
 * @return une référence constante sur Reflexivite.
 */
const double& Materiau::reflexivite() const
{
  return Reflexivite;
}

/**
 * Permet de récupérer la valeur du coefficient de transparence.
 *
 * @return une référence constante sur Transparence.
 */
const double& Materiau::transparence() const
{
  return Transparence;
}

/**
 * Permet de récupérer la valeur de l'indice de réfraction.
 *
 * @return une référence constante sur Indice.
 */
const double& Materiau::indice() const
{
  return Indice;
}

/**
 * Permet de récupérer la valeur du coefficient de diffusion.
 *
 * @return une référence constante sur Regularite.
 */
const double& Materiau::regularite() const
{
  return Regularite;
}

/**
 * Permet de récupérer la valeur de l'amplitude de la perturbation sur
 * la normale.
 *
 * @return une référence constante sur Bruit.
 */
const double& Materiau::bruit() const
{
  return Bruit;
}

/*
 * Permet de récupérer la valeur de la Couleur.
 *
 * @return la valeur de Color.
 */
// Couleur Materiau::couleur(const Coord &c)
// {
//   return Color;
// }

void Materiau::setReflexivite( const double& reflexivite )
{
  Reflexivite = borne( reflexivite, 0., 1. );
}

void Materiau::setTransparence( const double& transparence )
{
  Transparence = borne( transparence, 0., 1. );
}

void Materiau::setIndice( const double& indice )
{
  Indice = borne( indice, 0., 100. );
}

void Materiau::setRegularite( const double& regularite )
{
  Regularite = borne( regularite, 0., 1. );
}

void Materiau::setBruit( const double& bruit )
{
  Bruit = borne( bruit, 0., 1. );
}


/**
 * Permet de savoir si un Materiau est transparent ou non. Pour
 * accélérer les possibles calculs, on met une coupure à
 * \f$10^{-7}\f$sur la valeur maximale que peut avoir le coefficient
 * de transparence pour un objet totalement opaque.
 */
bool Materiau::estTransparent() const
{
  return Transparence < 1.e-7 ? false : true;
}

/**
 * Calcule la Couleur d'un point du Materiau en fonction de la Couleur
 * qui l'éclaire et l'angle sous lequel on voit ce point.
 *
 * @param c Couleur de la Lumiere qui éclaire le point.
 * @param angle valeur de l'angle (en radian) d'incidence du Rayon.
 * @param inters point de la surface (nécessaire pour obtenir la
 * Couleur).
 * @return la Couleur calculée d'après la Couleur du Materiau, celle
 * de la Lumiere et de l'angle de diffusion.
 */
Couleur Materiau::lumiereDiffusee( const Couleur& c, const double& angle,
				   const Coord &inters )
{
  Couleur a( couleur(inters) );
  //cerr << "Couleur point : " << a << endl;
  a.eclairee(c);
  //cerr << "Couleur écl   : " << a << endl;
  a.attenue( abs( Regularite * cos( angle ) ) );
  //cerr << "Couleur atten : " << a << endl;
  return a;
}

/**
 * Calcule la Couleur réfléchie par le Materiau.
 *
 * @param c Couleur "incidente" sur le Materiau.
 * @return une référence sur la Couleur atténuée par le coefficient de
 * réflexion.
 */
Couleur& Materiau::lumiereReflechie( Couleur& c )
{
  return c.attenue( Reflexivite );
  //return c;
}

/**
 * Calcule la Couleur qui est perçue à travers le Materiau.
 *
 * @param c Couleur "incidente" sur le Materiau.
 * @param inters point de la surface (nécessaire pour obtenir la
 * Couleur).
 * @return la Couleur résultant de l'éclairage de la Couleur
 * intrinsèque au Materiau par la source lumineuse. On atténue encore
 * le résultat par le coefficient de transparence.
 */
Couleur Materiau::lumiereRefractee( const Couleur& c, const Coord &inters )
{
  Couleur a;
  a = couleur( inters );
  a.eclairee( c );
  a.attenue( Transparence );
  return a;
}

/**
 * Permet de savoir quel est le type de l'instance courante.
 *
 * @return une référence constante sur Type.
 */
const TypeMateriau& Materiau::type() const
{
  return Type;
}
